/*
Copyright 2010,2011 Matt Van Der Westhuizen

This file is part of CGLL.

CGLL is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

CGLL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with CGLL.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.chaoticengine.cgll.entity.component;

import org.chaoticengine.cgll.entity.Entity;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.state.StateBasedGame;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.chaoticengine.cgll.ui.Widget;
import org.newdawn.slick.geom.Vector2f;

public class ComponentManager {
    // Components
    private Entity                     owner            = null;
    private ArrayList<IComponent>       allComponents    = null;
    private ArrayList<IActiveComponent> activeComponents = null;
    private ArrayList<ICollisionComponent> collisionComponents = null;
    private ArrayList<IRenderComponent> renderComponents = null;
    private ArrayList<IMovementComponent> moveComponents = null;
    private ArrayList<IUserInterfaceComponent> uiComponents = null;
    private Map<String, IComponent> specialComponents = null;

    public ComponentManager(Entity owner) {
        this.owner = owner;
        this.allComponents = new ArrayList<IComponent>();
        this.activeComponents = new ArrayList<IActiveComponent>();
        this.collisionComponents = new ArrayList<ICollisionComponent>();
        this.renderComponents = new ArrayList<IRenderComponent>();
        this.moveComponents = new ArrayList<IMovementComponent>();
        this.uiComponents = new ArrayList<IUserInterfaceComponent>();
        this.specialComponents = new HashMap<String, IComponent>();
    }

    public void addComponents(List<IComponent> comps) {
        for (int i = 0; i < comps.size(); i++) {
            addComponent(comps.get(i));
        }
    }

    public void addComponent(IComponent c) {
        // General component addition
        if (allComponents.contains(c)) {
            return;
        }

        allComponents.add(c);
        c.setOwner(owner);

        // Check if the component is specialised
        if (c instanceof IRenderComponent) {
            renderComponents.add((IRenderComponent) c);
        }

        if (c instanceof IActiveComponent) {
            activeComponents.add((IActiveComponent) c);
        }

        if (c instanceof ICollisionComponent) {
            collisionComponents.add((ICollisionComponent) c);
        }

        if (c instanceof IMovementComponent) {
            moveComponents.add((IMovementComponent) c);
        }

        if (c instanceof IUserInterfaceComponent) {
            uiComponents.add((IUserInterfaceComponent) c);
        }
    }

    public void removeComponent(IComponent c) {

        // General component removal
        allComponents.remove(c);

        // Check if the component was specialised
        if (c instanceof IRenderComponent) {
            renderComponents.remove((IRenderComponent)c);
        }

        if (c instanceof IActiveComponent) {
            activeComponents.remove((IActiveComponent)c);
        }

        if (c instanceof ICollisionComponent) {
            collisionComponents.remove((ICollisionComponent)c);
        }

        if (c instanceof IMovementComponent) {
            moveComponents.remove((IMovementComponent) c);
        }

        if (c instanceof IUserInterfaceComponent) {
            uiComponents.remove((IUserInterfaceComponent) c);
        }
    }

    /**
     * Removes all components managed by this ComponentManager and removes its
     * reference to its owner. Should be called when entities are destroyed.
     */
    public void destroy() {
        this.allComponents.clear();
        this.activeComponents.clear();
        this.collisionComponents.clear();
        this.renderComponents.clear();
        this.moveComponents.clear();
        this.uiComponents.clear();
    }

    public final List<IComponent> getComponents() {
        return (this.allComponents);
    }

    public void setComponents(List<IComponent> comps) {
        allComponents = new ArrayList<IComponent>();
        
        Iterator<IComponent> compIt = comps.iterator();
        while (compIt.hasNext()) {
            this.addComponent(compIt.next());
        }
    }

    /** Returns true if the entity has at least one RenderComponent. */
    public boolean mustRender() {
        return (renderComponents.size() > 0);
    }

    /** Render all RenderComponents. */
    public void render(GameContainer gc, StateBasedGame sb, Graphics gr) {
        if (!owner.isVisible()) {
            return;
        }

        for (int i = 0; i < renderComponents.size(); i++) {
            renderComponents.get(i).render(gc, sb, gr);
        }
    }

    /** Returns true if the entity has at least one ActiveComponent. */
    public boolean mustUpdate() {
        return (activeComponents.size() > 0);
    }

    /** Updates all ActiveComponents. */
    public void update(GameContainer gc, StateBasedGame sb, int delta) {
        for (int i = 0; i < activeComponents.size(); i++) {
            activeComponents.get(i).update(gc, sb, delta);
        }
    }

    /** Returns true if the entity has at least one CollisionComponent. */
    public boolean canCollide() {
        return(collisionComponents.size() > 0);
    }

    /** Handles a collision with entity e, with all CollisionComponents. */
    public void handleCollision(Entity e) {
        for (int i = 0; i < collisionComponents.size(); i++) {
            collisionComponents.get(i).handleCollision(e);
        }
    }

    /** Returns true if the entity has at least one MovementComponent. */
    public boolean canMove() {
        return(moveComponents.size() > 0);
    }

    /**
     * Returns the current velocity of the object based on the velocities of
     * all MovementComponents.
     *
     * @return The current velocity of the entity.
     */
    public Vector2f getVelocity() {
        Vector2f result = new Vector2f();
        for (int i = 0; i < moveComponents.size(); i++) {
            result.add(moveComponents.get(i).getVelocity());
        }
        return(result);
    }

    /** Returns all commands added by components. */
    public Map<String, Command> getCommands() {
        Map<String, Command> result = new HashMap<String, Command>();

        for (int i = 0; i < activeComponents.size(); i++) {
            result.putAll(activeComponents.get(i).getCommands());
        }

        return (result);
    }

    /** Returns all user interface widgets added by components. */
    public List<Widget> getWidgets() {
        List<Widget> result = new ArrayList<Widget>(5);

        for (int i = 0; i < uiComponents.size(); i++) {
            List<Widget> widgets = uiComponents.get(i).getWidgets();
            result.addAll(widgets);
        }

        Collections.sort(result);

        return(result);
    }

    public void registerSpecialComponent(String label, Component c) {
        if (allComponents.contains(c)) {
            specialComponents.put(label, c);
        }
    }

    public IComponent getSpecialComponent(String label) {
        return(specialComponents.get(label));
    }

    public void removeSpecialComponent(String label) {
        IComponent c = getSpecialComponent(label);

        if (c != null) {
            removeComponent(c);
        }
    }
}